package org.jeecg.modules.online.config.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.system.query.MatchTypeEnum;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.system.util.JeecgDataAutorUtils;
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.online.cgform.entity.OnlCgformField;
import org.jeecg.modules.online.cgform.service.IOnlCgformFieldService;
import org.jeecg.modules.online.cgform.util.CgformUtil;
import org.jeecg.modules.online.config.model.OnlineFieldConfig;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;

/**
 * sql转为占位符格式工具类
 * 条件处理器 = 页面查询条件 + 权限数据 + 高级查询
 * 支持系统变量替换
 * @Author taoYan
 * @Date 2021/10/15
 **/
@Data
@Slf4j
public class ConditionHandler {

    /**别名*/
    private String alias;

    /**不带.的别名*/
    private String aliasNoPoint;

    /**数据库类型*/
    private String dataBaseType;

    /**
     * 是否支持 时间字符串 搜索
     */
    private boolean dateStringSearch;

    /**字段集合*/
    private List<OnlineFieldConfig> fieldList;

    /**必须查询的列*/
    private List<String> needList;

    /** 权限数据 */
    private List<SysPermissionDataRuleModel> authDatalist;

    /** 请求传的参数*/
    private Map<String,Object> reqParams;

    /** 执行的sql条件语句 */
    private StringBuffer sql;

    /** 高级查询sql */
    private StringBuffer superQuerySql;

    /** 执行sql需要的参数*/
    private Map<String, Object> sqlParams;

    /** mybatis/jdbcTemplate */
    private String daoType;

    /** 是否是高级查询 */
    private boolean superQuery;

    /**
     * 每个条件之间的关系 and/or 高级查询会有区别 所以只要在handlePageSearch内的方法 都用该值 其他地方还是拼and
     */
    private String matchType;

    /** 1.online报表  2.online表单*/
    private int usePage;

    /**是否是第一个条件  第一个条件不用拼and*/
    private boolean first;

    /**
     * 从前端传进来的参数前缀 联合查询会带表名@
     */
    private String paramPrefix;

    /**sql中一个字段出现两次  age>#{age} && age<#{age} 这种情况#{age}就有多个 用以下map存储记录 加序号区分#{age1} #{age2}*/
    private Map<String, String> duplicateSqlNameRecord;

    /**同duplicateSqlNameRecord  只不过上面是sql语句中的  这个map是记录执行sql参数里的参数名 */
    private Map<String, String> duplicateParamNameRecord;


    /**
     * 默认构造器
     */
    public ConditionHandler(){}


    /**
     * 构造器 online报表 用
     * @param alias 别名 alias 带.
     * @param dataBaseType 数据库类型
     */
    public ConditionHandler(String alias, String dataBaseType){
        this.alias = alias;
        this.aliasNoPoint = alias.replace(".", "");
        this.dataBaseType = dataBaseType;
        this.dateStringSearch = supportDateStringSearch(dataBaseType);
        sql = new StringBuffer();
        sqlParams = new HashMap<>();
        this.authDatalist = null;
        this.needList = null;
        this.matchType = " AND ";
        this.usePage = 1;
        this.first = true;
        this.paramPrefix = "";
        this.duplicateSqlNameRecord = new HashMap<>();
        this.duplicateParamNameRecord = new HashMap<>();
    }

    /***
     * online表单 用
     * @param alias
     */
    public ConditionHandler(String alias){
        this(alias, null);
        this.usePage = 2;
    }


    /***
     * superQuery 用
     * @param alias
     */
    public ConditionHandler(String alias, boolean superQuery, String matchType){
       this(alias, null);
       this.superQuery = superQuery;
       this.matchType = " "+matchType+" ";
       this.usePage = 2;
    }

    /**
     * 获取查询条件sql语句 online报表用
     */
    public String getConditionSql(List<OnlineFieldConfig> fieldList, Map<String,Object> params){
        handlePageSearch(fieldList, params);
        handleOnlineReportAuthData();
        return sql.toString();
    }


    /**
     * 获取查询条件sql语句 online表单用 非联合查询
     */
    public String getConditionSql(List<OnlineFieldConfig> fieldList, Map<String,Object> params, List<SysPermissionDataRuleModel> authDatalist){
        this.setAuthDatalist(authDatalist);
        // 页面查询
        handlePageSearch(fieldList, params);
        // online权限
        handleOnlineTableAuthData(fieldList);
        // 高级查询
        handleSuperQuery(params);
        return sql.toString();
    }

    /**
     * 获取查询条件sql语句 online表单用 联合查询
     */
    public String getConditionSql(List<OnlineFieldConfig> fieldList, Map<String,Object> params, List<SysPermissionDataRuleModel> authDatalist, String paramPrefix){
        this.setAuthDatalist(authDatalist);
        this.paramPrefix = paramPrefix;
        // 页面查询
        handlePageSearch(fieldList, params);
        // online权限
        handleOnlineTableAuthData(fieldList);
        return sql.toString();
    }


    /**
     * 只处理字段
     * @param fieldList
     * @param params
     * @return
     */
    public String getPageSearchSql(List<OnlineFieldConfig> fieldList, Map<String,Object> params){
        handlePageSearch(fieldList, params);
        return sql.toString();
    }

    /**
     * 获取高级查询的sql语句
     * @param fieldList
     * @return
     */
    public String getSuperQuerySql(List<OnlineFieldConfig> fieldList){
        if(superQuery){
            for(OnlineFieldConfig item: fieldList){
                String name = item.getName();
                String value = item.getVal();
                if(value!=null){
                    QueryRuleEnum rule = QueryRuleEnum.getByValue(item.getRule());
                    if (rule == null) {
                        rule = QueryRuleEnum.EQ;
                    }
                    handleSingleSearchValue(name, item.getType(), value, rule);
                }
            }
        }
        return sql.toString();
    }


    /**
     * 处理 页面查询条件
     */
    public void handlePageSearch(List<OnlineFieldConfig> fieldList, Map<String,Object> params){
        for(OnlineFieldConfig item: fieldList){
            String name = item.getName();
            String type = item.getType();

            //如果needlist 包含这个字段 需要将此字段设置为可单条件查询
            if(needList!=null && needList.contains(name)){
                item.setIsSearch(1);
                item.setMode("single");
            }

            //如果是外键字段 需要设置为可查询的
            if(StringUtil.isNotEmpty(item.getMainField()) && StringUtil.isNotEmpty(item.getMainTable())){
                item.setIsSearch(1);
                item.setMode("single");
            }

            //1.判断是否查询
            if(1 == item.getIsSearch()){
                //2.判断查询类型
                if("group".equals(item.getMode())){
                    // TODO 测试范围查询
                    String paramBegin = name + "_begin";
                    Object value_begin = params.get(this.paramPrefix+paramBegin);
                    if(null != value_begin){
                        // and a.name >=
                        this.addFieldName2Sql(name, " >= ");
                        handleSearchValue(paramBegin, type, value_begin);
                    }

                    String paramEnd = name + "_end";
                    Object value_end = params.get(this.paramPrefix+paramEnd);
                    if(null != value_end){
                        this.addFieldName2Sql(name, " <= ");
                        handleSearchValue(paramEnd, type, value_end);
                    }
                }else{
                    // 单查询
                    Object value = params.get(this.paramPrefix+name);
                    if(value!=null){
                        // 获取查询控件类型
                        String view = item.getView();
                        if("list_multi".equals(view)){
                            // 判断是否是 多选--online表单查询专有
                            this.handleMultiSelectSql(name, value);
                        }else if("popup".equals(view)){
                            // 判断是否是 popup--online表单查询专有
                            this.handlePopupSql(name, value);
                        }else{
                            handleSingleSearchValue(name, type, value);
                        }
                    }
                }
            }
        }
    }


    /**
     * online表单开发的数据 获取sql前 可以调用一下这个
     * @param authDatalist
     */
    public void setAuthList(List<SysPermissionDataRuleModel> authDatalist){
        this.authDatalist = authDatalist;
    }

    /**
     * 处理 权限数据 online报表用
     */
    private void handleOnlineReportAuthData(){
         List<SysPermissionDataRuleModel> list = JeecgDataAutorUtils.loadDataSearchConditon();
        if(list != null && list.size()>0){
            for(int i=0;i<list.size();i++){
                SysPermissionDataRuleModel dataRule = list.get(i);
                if(dataRule==null){
                    // 第一个元素为null 视为无权限数据 重现-未知-TODO
                    break;
                }
                String ruleValue = dataRule.getRuleValue();
                if(oConvertUtils.isEmpty(ruleValue)){
                    continue;
                }
                String condition = dataRule.getRuleConditions();
                if(QueryRuleEnum.SQL_RULES.getValue().equals(condition)){
                    // 自定义sql直接取value
                    this.addFieldName2Sql("", QueryGenerator.getSqlRuleValue(ruleValue));
                }else{
                    QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
                    String type = "Integer";
                    ruleValue = ruleValue.trim();
                    if(ruleValue.startsWith("'") && ruleValue.endsWith("'")){
                        type = "string";
                        ruleValue = ruleValue.substring(1,ruleValue.length()-1);
                    //update-beign-author:taoyan date:20211227 for: JTC-34 【online报表】数据权限 #{sys_org_code}获取不到
                    }else if(ruleValue.startsWith("#{") && ruleValue.endsWith("}")){
                        type = "string";
                    }
                    //update-end-author:taoyan date:20211227 for: JTC-34 【online报表】数据权限 #{sys_org_code}获取不到
                    // 如果value有#{} 需要替换系统变量
                    String value = QueryGenerator.converRuleValue(ruleValue);
                    handleSingleSearchValue(dataRule.getRuleColumn(), type, value, rule);
                }
            }
        }
    }

    /**
     * 根据字段名 找字段配置
     * @param name
     * @param fieldList
     * @return
     */
    private OnlineFieldConfig getOnlineFieldByName(String name, List<OnlineFieldConfig> fieldList){
        if(fieldList!=null && name!=null){
            String underLineName =  oConvertUtils.camelToUnderline(name);
            for(int i=0;i<fieldList.size();i++){
                OnlineFieldConfig onlineField = fieldList.get(i);
                String fieldName = onlineField.getName();
                // 根据字段名找字段配置  权限里的字段，文档要求配置成下划线 但是可能被配置成驼峰的  这里兼容一下
                if(name.equals(fieldName) || underLineName.equals(fieldName)){
                    return onlineField;
                }
            }
        }
        return null;
    }

    /**
     * 处理 权限数据 online表单用
     */
    private void handleOnlineTableAuthData(List<OnlineFieldConfig> fieldList){
        //权限查询
        List<SysPermissionDataRuleModel> list = this.authDatalist;
        if(list == null){
            list = JeecgDataAutorUtils.loadDataSearchConditon();
        }
        if(list!=null && list.size()>0){
            for(int i=0;i<list.size();i++){
                SysPermissionDataRuleModel dataRule = list.get(i);
                if(dataRule==null){
                    break;
                }
                String ruleValue = dataRule.getRuleValue();
                if(oConvertUtils.isEmpty(ruleValue)){
                    continue;
                }
                String condition = dataRule.getRuleConditions();
                if(QueryRuleEnum.SQL_RULES.getValue().equals(condition)){
                    // 自定义sql直接取value
                    this.addFieldName2Sql("", QueryGenerator.getSqlRuleValue(ruleValue));
                }else{
                    String fieldName = dataRule.getRuleColumn();
                    OnlineFieldConfig item = getOnlineFieldByName(fieldName, fieldList);
                    if(item!=null){
                        // 如果value有#{} 需要替换系统变量
                        String value = QueryGenerator.converRuleValue(ruleValue);
                        QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
                        handleSingleSearchValue(item.getName(), item.getType(), value, rule);
                    }
                }
            }
        }
    }


    /**
     * 单条件查询
     */
    private void handleSingleSearchValue(String name, String type, Object value){
        handleSingleSearchValue(name, type, value, null);
    }


    /**
     * 单条件查询
     */
    private void handleSingleSearchValue(String name, String type, Object value, QueryRuleEnum rule){
        if(value!=null){
            String str = value.toString();
            // 查询值是否被带上了一些条件规则 > ! < 之类的
            // 不需要额外处理的：高级查询的条件值、数据权限的条件值 都是false
            boolean isWarpValue = false;
            if(rule==null){
                isWarpValue = true;
                rule = QueryGenerator.convert2Rule(value);
            }
            if(isWarpValue){
                str = str.trim();
            }
            switch (rule) {
                case GT:
                case LT:
                    this.addFieldName2Sql(name, rule.getValue());
                    if(isWarpValue){
                        str = str.substring(1);
                    }
                    handleSearchValue(name, type, str);
                    break;
                case GE:
                case LE:
                    this.addFieldName2Sql(name, rule.getValue());
                    if(isWarpValue){
                        str = str.substring(2);
                    }
                    handleSearchValue(name, type, str);
                    break;
                case EQ:
                    this.addFieldName2Sql(name, rule.getValue());
                    handleSearchValue(name, type, str);
                    break;
                case EQ_WITH_ADD:
                    this.addFieldName2Sql(name, rule.getValue());
                    if(isWarpValue){
                        str = str.replaceAll("\\+\\+", ",");
                    }
                    handleSearchValue(name, type, str);
                    break;
                case NE:
                    this.addFieldName2Sql(name, " <> ");
                    if(isWarpValue){
                        str = str.substring(1);
                    }
                    handleSearchValue(name, type, str);
                    break;
                case IN:
                    this.addFieldName2Sql(name, " in ");
                    handleConditionIn(name, type, str);
                    break;
                case LIKE:
                case RIGHT_LIKE:
                case LEFT_LIKE:
                    this.addFieldName2Sql(name, " like ");
                    if(isWarpValue){
                        handleConditionLike(name, str);
                    }else{
                        handleConditionLike(name, str, rule);
                    }
                    break;
                default:
                    this.addFieldName2Sql(name, " = ");
                    handleSearchValue(name, type, str);
                    break;
            }
        }
    }

    /** in查询 */
    private void handleConditionIn(String name, String type, String str){
        String[] arr = str.split(",");
        if(arr.length==0){
            this.addFieldValue2Sql("('')");
        }else{
            String paramKeyExp = "foreach_%s_%s";
            // 括号里面的内容
            String content = "";
            for(int i=0;i<arr.length;i++){
                String temp = arr[i].trim();
                String paramKey = String.format(paramKeyExp, name, i);
                if(i>0){
                    content+=",";
                }
                String realParamKey = getRealKeyForSql(paramKey);
                if("jdbcTemplate".equals(this.daoType)){
                    content+=":"+realParamKey;
                }else{
                    /*
                    String insql = "<foreach collection=\"%s\" index=\"index\" item=\"item\" open=\"(\" separator=\",\" close=\")\">#{item}</foreach>";
                    String.format(insql, getSqlParamKey(name))
                    */
                    // 最有方案是上面传 foreach 但是实际测试 并不能解析成功 所以这里只能使用 手动拼接
                    content+= "#{"+getSqlParamKey(realParamKey)+"}";
                }
                if("Long".equals(type) || "Integer".equals(type)){
                    this.addParam2Map(paramKey, Integer.parseInt(temp));
                }else{
                    this.addParam2Map(paramKey, temp);
                }
            }
            this.addFieldValue2Sql("(" + content + ")");
        }
    }

    /** like查询 */
    private void handleConditionLike(String name, String str){
        // "%"?"%" 这种写法 部分数据库不支持 故直接获取实际的like字符串 传进去
        String likeSql = getSqlParamPlaceholder(name, "VARCHAR");
        this.addFieldValue2Sql(likeSql);
        String temp = "";
        if((str.startsWith("*") && str.endsWith("*")) || str.startsWith("%") && str.endsWith("%")) {
            temp = "%"+str.substring(1,str.length()-1)+"%";
        }else if(str.startsWith("*") || str.startsWith("%")) {
            temp = "%" + str.substring(1);
        }else if(str.endsWith("*") || str.endsWith("%")) {
            temp = str.substring(0,str.length()-1) + "%";
        }else {
            temp = "%"+str+"%";
        }
        this.addParam2Map(name, temp);
    }

    /**
     * 高级查询 、权限数据 like
     * @param rule
     * @param name
     * @param str
     */
    private void handleConditionLike(String name, String str, QueryRuleEnum rule){
        String likeSql = getSqlParamPlaceholder(name, "VARCHAR");
        this.addFieldValue2Sql(likeSql);
        if(rule==QueryRuleEnum.LEFT_LIKE){
            // 左模糊
            this.addParam2Map(name, "%"+str);
        } else if(rule==QueryRuleEnum.RIGHT_LIKE){
            // 右模糊
            this.addParam2Map(name, str+"%");
        } else {
            // 全模糊
            this.addParam2Map(name, "%"+str+"%");
        }
    }

    /** 处理条件值 */
    private void handleSearchValue(String name, String type, Object value){
        String type_lowerCase = type.toLowerCase();
        if(isNumber(type)){
            // 因为这里直接拼的sql，所以需要数字校验，校验通过才添加条件
            if(CgformUtil.isNumeric(value.toString())){
                //如果数字类型直接拼在SQL后面
                this.addFieldValue2Sql(value.toString());
            }else{
                this.addFieldValue2Sql("''");
                //log.info("请注意，查询条件值"+value.toString()+"非数字！");
            }
        }else if("datetime".equals(type_lowerCase)){
            Date datetime = DateUtils.str2Date(value.toString(),DateUtils.datetimeFormat.get());
            addDatetimeParam(name, datetime);
        }else if("date".equals(type_lowerCase)){
            Date date = DateUtils.str2Date(value.toString(),DateUtils.date_sdf.get());
            addDateParam(name, date);
        }else{
            String temp = value.toString().trim();
            if(temp.startsWith("'") && temp.endsWith("'") && this.usePage==1){
                // 有一种特殊情况 popup调用都走这个逻辑 参数传递的可能是“‘admin’”这种格式的
                this.addFieldValue2Sql(temp);
            }else{
                addParam(name, temp);
            }
        }
    }

    /**
     * 拼接字段到sql中去  and a.age >=
     * @param name age
     * @param condition >=，也有可能是全sql:age>18,这种情况下name为空字符串
     */
    private void addFieldName2Sql(String name, String condition){
        addFieldName2Sql(name, condition, this.matchType);
    }

    /**
     * 多个高级查询子表in查询之间 拼条件 自己传入
     * 拼接字段到sql中去  and a.age >=
     * @param name age
     * @param condition >=，也有可能是全sql:age>18,这种情况下name为空字符串
     */
    private void addFieldName2Sql(String name, String condition, String matchType){
        if(this.first==true){
            this.first = false;
        }else{
            this.sql.append(matchType);
        }
        if(name.length()>0){
            // 传name 拼接条件
            this.sql.append(alias).append(name).append(condition);
        }else{
            // 不传name 说明是全sql
            this.sql.append(" ").append(condition).append(" ");
        }
    }

    /**
     * 拼接字段的值 到sql中去  #{param.field}
     * 18
     * 在其他地方不允许直接调用 this.sql.append 需要通过该方法或者addFieldName2Sql去操作 sql对象
     */
    private void addFieldValue2Sql(String value){
        this.sql.append(value);
    }

    /**
     * 获取参数占位符
     * :name 表示 springJdbc占位符
     * #{name} 表示 mybatis占位符
     */
    private String getSqlParamPlaceholder(String key, String jdbcType){
        // 处理重复的条件名
        key = this.getRealKeyForSql(key);
        if("jdbcTemplate".equals(daoType)){
            return ":"+key;
        }else{
            String paramKey = getSqlParamKey(key);
            if(jdbcType==null){
                return String.format("#{%s}", paramKey);
            }else{
                return String.format("#{%s, jdbcType=%s}", paramKey, jdbcType);
            }
        }
    }

    /**
     * 获取sql中参数取值的字符串
     */
    private String getSqlParamKey(String key){
        return "param." + getParamKey(key);
    }

    /**
     * 将参数值放到map
     * @param key
     * @param paramValue
     */
    private void addParam2Map(String key, Object paramValue){
        // 处理重复的条件名
        key = this.getRealKeyForParam(key);
        sqlParams.put(getParamKey(key), paramValue);
    }

    /**
     * 获取参数map的key 同时也是sql中#{key} 表达式中的取值key
     * @param key
     * @return
     */
    private String getParamKey(String key){
        // 防止参数名重复
        if(this.usePage==1){
            return key;
        }else{
            return this.aliasNoPoint+"_"+key;
        }
    }


    /**
     * 添加时间类型的参数
     */
    private void addDatetimeParam(String key, Object paramValue){
        if(paramValue!=null){
            String str = getSqlParamPlaceholder(key, "TIMESTAMP");
            this.addFieldValue2Sql(str);
            this.addParam2Map(key, paramValue);
        }
    }

    /**
     * 添加日期类型的参数
     */
    private void addDateParam(String key, Object paramValue){
        if(paramValue!=null){
            String str = getSqlParamPlaceholder(key, "DATE");
            this.addFieldValue2Sql(str);
            this.addParam2Map(key, paramValue);
        }
    }

    /**
     * 添加普通类型的参数
     */
    private void addParam(String key, Object paramValue){
        if(paramValue!=null){
            String str = getSqlParamPlaceholder(key, null);
            this.addFieldValue2Sql(str);
            this.addParam2Map(key, paramValue);
        }
    }

    /**
     * 判断是否是数值  这个暂时是基于online报表做的 online报表字段里 就long和int是数值
     */
    private boolean isNumber(String type){
        return "Long".equals(type) || "Integer".equals(type) || "int".equals(type) || "double".equals(type) || "BigDecimal".equals(type) || "number".equals(type);
    }

    /**
     * 判断 不支持 时间字符串搜索的
     */
    private boolean supportDateStringSearch(String dataBaseType){
        // 如果是 oracle数据库 则不支持时间字符串比较
       return !DataBaseConstant.DB_TYPE_ORACLE.equals(dataBaseType);
    }

    public static String replaceSqlPageParam(String sql, long firstParam){
        return sql.replaceFirst("\\?", String.valueOf(firstParam));
    }

    public static String replaceSqlPageParam(String sql, long firstParam, long secondParam){
        sql = sql.replaceFirst("\\?", String.valueOf(firstParam));
        return sql.replaceFirst("\\?", String.valueOf(secondParam));
    }


    /**
     * 处理下拉多选的值 不处理jdbcTemplate
     * AND ( NAME LIKE '%5,%' OR NAME LIKE '%,5%' OR NAME = '5')
     * @param name
     * @param value
     */
    private void handleMultiSelectSql(String name, Object value){
        if(value!=null){
            String[] vals = value.toString().split(",");
            String multiSelectSql = "";
            String field = this.alias + name;
            for(int i=0;i<vals.length;i++){
                // TODO SQLSERVER下这种写法是否支持 考虑到字典数据非中文 大体没问题
                String temp = field + " like '%"+ vals[i]+",%' or " + field + " like '%,"+ vals[i]+"%' or " + field + " = '"+vals[i]+"'";
                if(multiSelectSql.length()==0){
                    multiSelectSql = temp;
                }else{
                    multiSelectSql = multiSelectSql + " or " + temp;
                }
            }
            if(multiSelectSql.length()>0){
                String str = "("+multiSelectSql+")";
                this.addFieldName2Sql("", str);
            }
        }
    }

    /**
     * 处理 popup  不处理jdbcTemplate
     * 【test,jeecg】转为【 ? like '%test%' AND ? like '%jeecg%'】
     * @param name
     * @param value
     */
    private void handlePopupSql(String name, Object value){
        if(value!=null){
            String field = this.alias + name;
            String popupSql = "";
            String paramKeyExp = "popup_%s_%s";
            String[] vals = value.toString().split(",");
            for(int i=0;i<vals.length;i++){
                String paramKey = String.format(paramKeyExp, name, i);
                String likeSql = getSqlParamPlaceholder(paramKey, "VARCHAR");
                String paramValue = "%"+vals[i]+"%";
                this.addParam2Map(paramKey, paramValue);

                String temp = field + " like " + likeSql;
                if(popupSql.length()==0){
                    popupSql = temp;
                }else{
                    popupSql += " and " + temp;
                }
            }
            if(popupSql.length()>0){
                String str = "("+popupSql+")";
                this.addFieldName2Sql("", str);
            }
        }
    }

    public String subTableStr;



    /**
     * 处理高级查询
     * 拿到 高级查询的参数
     * 1.分隔，将同个表名的参数分在一起处理
     * 2.转换，将高级查询的结构转成OnlineFieldConfig的结构处理 通用性强
     * 3.合并，步骤12处理完的结果 需要合并到主sql里，sql执行参数也是
     *
     * 高级查询 和 普通查询区别：
     * 高级查询一个字段是下拉多选的(popup同理)，
     * 然后，普通查询选多个，系统会走like查询；
     * 高级查询只能根据 选的条件 走具体的查询，所以对于这种类型的 建议用户使用时选一项 然后走【包含】条件
     */
    private void handleSuperQuery(Map<String, Object> requestParams){

        Object superQueryMatchTypeObject = requestParams.get("superQueryMatchType");
        MatchTypeEnum mte = MatchTypeEnum.getByValue(superQueryMatchTypeObject);
        if (mte == null) {
            mte = MatchTypeEnum.AND;
        }
        // 取出查询条件
        Object superQueryParamsObject = requestParams.get("superQueryParams");
        if (superQueryParamsObject == null || StringUtils.isBlank(superQueryParamsObject.toString())) {
            return;
        }

        String superQueryParams = null;
        try {
            superQueryParams = URLDecoder.decode(superQueryParamsObject.toString(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return;
        }
        JSONArray array = JSONArray.parseArray(superQueryParams);
        IOnlCgformFieldService service = SpringContextUtils.getBean(IOnlCgformFieldService.class);

        // 将所有表名存到一个list中去
        List<String> ls = new ArrayList<>();
        // 添加一个元素标识主表
        ls.add("JEECG_SUPER_QUERY_MAIN_TABLE");
        if(subTableStr!=null && !"".equals(subTableStr)){
            // 如果有子表
            String[] arr = subTableStr.split(",");
            for(String str: arr){
                ls.add(str);
            }
        }

        // 子表 map
        Map<String, JSONObject> fkeyInfo = new HashMap<>();

        StringBuffer sb = new StringBuffer();
        // 循环所有表 按表名分隔
        for(int j=0;j<ls.size();j++){
            String table = ls.get(j);

            List<OnlineFieldConfig> fieldList = new ArrayList<>();
            for (int i = 0; i < array.size(); i++) {
                JSONObject parameter = array.getJSONObject(i);
                String field = parameter.getString("field");
                if(oConvertUtils.isEmpty(field)){
                    continue;
                }
                String[] fieldArray = field.split(",");
                OnlineFieldConfig temp = new OnlineFieldConfig(parameter);
                // 没有使用逗号分割，说明是主表
                if ("JEECG_SUPER_QUERY_MAIN_TABLE".equals(table) && fieldArray.length == 1) {
                    // 过滤主表的查询条件
                    fieldList.add(temp);
                }else if(fieldArray.length==2 && fieldArray[0].equals(table)){
                    // 高级查询 field带逗号时 逗号前面的一定是表名 以此判断是不是该表的查询条件
                    fieldList.add(temp);
                    JSONObject subTable = fkeyInfo.get(table);
                    if (subTable == null) {
                        List<OnlCgformField> subTableFields = service.queryFormFieldsByTableName(table);
                        subTable = new JSONObject();
                        for (OnlCgformField cgformField : subTableFields) {
                            if (StringUtils.isNotBlank(cgformField.getMainTable())) {
                                subTable.put("subTableName", table);
                                subTable.put("subField", cgformField.getDbFieldName());
                                subTable.put("mainTable", cgformField.getMainTable());
                                subTable.put("mainField", cgformField.getMainField());
                            }
                        }
                        fkeyInfo.put(table, subTable);
                    }
                }
            }
            if(fieldList.size()>0){
                // 每循环一个 处理一个
                String tempAlias = j==0?this.alias: this.aliasNoPoint+j+".";
                ConditionHandler handler = new ConditionHandler(tempAlias, true, mte.getValue());
                // 将主查询的重复字段信息传到高级查询中 保证高级查询规则统一  高级查询必须最后处理  如果高级查询后面还需要处理其他的 还需要把这个传给他
                handler.setDuplicateParamNameRecord(this.getDuplicateParamNameRecord());
                handler.setDuplicateSqlNameRecord(this.getDuplicateSqlNameRecord());
                String tempSql = handler.getSuperQuerySql(fieldList);
                Map<String,Object> tempParam = handler.getSqlParams();
                if(tempSql!=null && tempSql.length()>0){
                    if(j==0){
                        // 如果是主表
                        //this.addFieldName2Sql("", "("+tempSql+")");
                        sb.append(" ").append(tempSql).append(" ");
                        this.sqlParams.putAll(tempParam);
                    }else{
                        JSONObject subTable = fkeyInfo.get(table);
                        String subTableName = subTable.getString("subTableName");
                        String subField = subTable.getString("subField");
                        String mainField = subTable.getString("mainField");

                        String subTableSqlTemplate = " %s in (select %s from %s %s where ";
                        String subTableSql = String.format(subTableSqlTemplate, mainField, subField, subTableName, this.aliasNoPoint+j);
                        // TODO 默认情况下  不同的表的条件 单独走in  然后拼and  即使设置的是or  最终拼出来的sql也是and
                        //  如果想改变  这里传入具体的matchType即可 但是随之的改变就是： 查询条件和高级查询条件之间的关系不再一定是and了  会随着传入的matchType而定
                        //this.addFieldName2Sql("", subTableSql + tempSql + ")");
                        this.sqlParams.putAll(tempParam);
                        sb.append(mte.getValue()).append(subTableSql).append(tempSql).append(") ");
                    }
                }
            }
        }
        // 保证 高级查询的逻辑在一起
        String finalSuperQuerySql = sb.toString();
        if(finalSuperQuerySql.length()>0){
            if(finalSuperQuerySql.startsWith("AND ")){
                finalSuperQuerySql = finalSuperQuerySql.substring(3);
            }else if(finalSuperQuerySql.startsWith("OR ")){
                finalSuperQuerySql = finalSuperQuerySql.substring(2);
            }
            this.addFieldName2Sql("", "("+finalSuperQuerySql+")");
        }
    }


    /**
     *  处理重复的查询条件名 获取实际sql中拼接的名字
     * @param key
     * @return
     */
    private String getRealKeyForSql(String key){
        return this.handleDuplicateNameBackRealKey(key, this.duplicateSqlNameRecord);
    }

    /**
     *  处理重复的查询条件名  获取实际执行sql的map对应的key
     * @param key
     * @return
     */
    private String getRealKeyForParam(String key){
        return this.handleDuplicateNameBackRealKey(key, this.duplicateParamNameRecord);
    }

    /**
     * 处理重复的查询条件名
     * @param key
     * @param map
     * @return
     */
    private String handleDuplicateNameBackRealKey(String key, Map<String, String> map){
        String realKey = map.get(key);
        if(realKey==null){
            realKey = key;
            map.put(key, key+"_1");
        }else{
            String numStr = realKey.substring(realKey.lastIndexOf("_") + 1);
            String nextKey =  key + "_" + (Integer.parseInt(numStr) + 1);
            map.put(key, nextKey);
        }
        return realKey;
    }

}
